package gagak.framework.web.paramresovler.converter;

import gagak.framework.web.exception.HttpMessageNotWritableException;
import gagak.framework.web.support.http.HttpHeaders;
import gagak.framework.web.paramresovler.requestbody.HttpOutputMessage;
import gagak.framework.web.paramresovler.requestbody.StreamingHttpOutputMessage;
import gagak.framework.web.support.http.MediaType;
import gagak.framework.web.annonation.Nullable;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;

/*
 * @program: ce
 * @description:
 * @author: houhong
 * @create: 2022-12-01 16:02
 */
public abstract  class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T>
        implements GenericHttpMessageConverter<T> {


    /*
     * @see #setSupportedMediaTypes
     */
    protected AbstractGenericHttpMessageConverter() {
    }

    /*
     * @param supportedMediaType the supported media type
     */
    protected AbstractGenericHttpMessageConverter(MediaType supportedMediaType) {
        super(supportedMediaType);
    }

    /*
     * @param supportedMediaTypes the supported media types
     */
    protected AbstractGenericHttpMessageConverter(MediaType... supportedMediaTypes) {
        super(supportedMediaTypes);
    }


    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
        return (type instanceof Class ? canRead((Class<?>) type, mediaType) : canRead(mediaType));
    }

    @Override
    public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {
        return canWrite(clazz, mediaType);
    }

    /*
     * This implementation sets the default headers by calling {@link #addDefaultHeaders},
     * and then calls {@link #writeInternal}.
     */
    @Override
    public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
                            HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

        final HttpHeaders headers = outputMessage.getHeaders();
        addDefaultHeaders(headers, t, contentType);

        if (outputMessage instanceof StreamingHttpOutputMessage) {
            StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
            streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
                @Override
                public void writeTo(final  OutputStream outputStream) throws IOException {
                    AbstractGenericHttpMessageConverter.this.writeInternal(t, type, new HttpOutputMessage() {
                        @Override
                        public OutputStream getBody() {
                            return outputStream;
                        }

                        @Override
                        public HttpHeaders getHeaders() {
                            return headers;
                        }
                    });
                }
            });
        }
        else {
            writeInternal(t, type, outputMessage);
            outputMessage.getBody().flush();
        }
    }

    @Override
    protected void writeInternal(T t, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        writeInternal(t, null, outputMessage);
    }

    /*
     * @param outputMessage
     * @throws IOException
     * @throws HttpMessageNotWritableException
     */
    protected abstract void writeInternal(T t, @Nullable Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;
}